くらめその情シス:社内基幹のIdPをAzureADに切り替えたおはなし
はじめに
どうも、情シスやってますアノテーションの徳道です。
社内のMDM移行もいろいろ目途がつき、2021年2月にGoogle WorkspaceやSalesforceなど社内基幹のシングルサインオンをAzureADに切り替えました。
1年近くほぼAzure関連の導入・運用検討をしつつ記事を書いてきましたが、ここが一つの区切りかな、というところです。
今回はXデーにAzureADをIdPとしたシングルサインオン設定の設定事例(主にハマったポイント)を紹介します。
Special Thanks:植木 和樹
基幹システムのシングルサインオンの構成
今回設定したシングルサインオンの構成を図で示します。SAML2.0認証が可能なSaaSについて今後追加されていくことになります。
IdPの設定準備
AzureADエンタープライズアプリケーションでSAMLによるシングルオン設定は以前の記事でも紹介しましたが、設定個所はさほど多くないです。
今回は情シス側で管理している2つのSaaSの設定を紹介します。
- Google Workspace
- Sansan
メジャーなIdPとメジャーなSaaSであるため、公式マニュアルや事例記事はたくさんありました。
サービスプロバイダ(SP)側では設定項目も少なくいじれるところがほとんどありません。
実際、今回も切替の際にはいろいろ試行錯誤がありましたが、SP側で設定を変えることはなく逆にIdP側では柔軟な設定ができる分、いろいろトライ&エラーがありました。
最終的に動作した設定と、ハマったポイントを見ていきましょう。
Google WorkspaceのSAML設定
AzureAD側の設定
識別子(Entity ID)と 応答URL(Assertion Consumer Servie URL)、サインオンURLを設定します。
基本的なSAML構成
- 識別子:google.com/a/(自社のドメイン名)
- 応答URL:https://www.google.com/a/(自社のドメイン名)/acs
- サインオンURL:https://www.google.com/a/(自社のドメイン名)/ServiceLogin?continue=https://mail.google.com
自社のドメイン名は、GoogleWorkspaceに設定している hogehoge.jpとかhogehoge.comなどです。
ユーザー属性とクレーム
ユーザー属性とクレームは一意のユーザーID、emailaddressとnameだけにしましょう(弊社のuser.principalnameはemail)。
ここはハマったポイントなので後で詳しく述べます。
SAML署名証明書
SAML署名証明書からBase64証明書をダウンロードしてGoogleWorkspace管理者に渡します。
ダウンロードするファイルはBase64エンコードされた証明書だけでOKです。
【注意ポイント】証明書の有効期限を必ず確認し、切れていれば作り直す必要があります。
所有者、ユーザーとグループ
「所有者」にAzureAD管理アカウントを、「ユーザーとグループ」には利用者全員が所属しているAzureのグループを割り当てておきます。
GoogleWorkspace側の設定
Google Adminの セキュリティ → サードパーティの ID プロバイダを使用したシングル サインオン(SSO)の設定 で設定します。
https://admin.google.com/ac/security/sso
設定はここの設定は4か所だけです。各設定をAzureADのログインページURL、ログアウトURLのリンクをコピペで張り付けます。
【AzureADの設定で表示されている】
パスワードリセットURLはAzureADのMy AccountのURLを張り付けておきました。
https://account.activedirectory.windowsazure.com/ChangePassword.aspx
ハマリポイント
設定項目も多くはないのですが、GoogleWorkspaceはIdPを1つしか設定できないため、事前の動作確認のできない本番一発勝負。
設定当日は動いてね!と祈りつつGmailを開き、AzureADでログインして画面遷移…がGmailは表示されません。。
なんともつれない・・・。このエラーを調べるとGoogle側では証明書がおかしい、というような回答が書かれていました。
「ログイン資格情報を確認できないため、このアカウントにはアクセスできません。」
しかし何度か証明書を作り直してアップロードしても変化なし。
Azure エンタープライズアプリケーションでのSAMLテストでは一見うまくいっているように見えました。
他に見えるところがわからないのでSAML要求(SAML AuthRequest)、SAML応答(SAML Response)のXMLファイルを調べてみることにしました。
Google WorkSpaceの場合はSP Initiated(ユーザーがGoogleのサイトにアクセスしたら認証要求をIdPに行う)です。
SAML要求はGoogleWorkSpaceからAzureへ、SAML応答はAzureからGoogleWorkSpaceへ、ということになります。
SAML要求を確認
<?xml version="1.0" encoding="UTF-8"?> <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="xxxxxxxxxxxxxxxxxxxx" Version="2.0" IssueInstant="yyyy-mm-ddT00:00:00Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" ProviderName="google.com" IsPassive="false" AssertionConsumerServiceURL="https://www.google.com/a/domain.jp/acs" ForceAuthn="true"> <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">google.com/a/domain.jp</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"/> </samlp:AuthnRequest>
ここではSPが要求しているユーザーIDのフォーマットであるsamlp:NameIDPolicy に注目。
urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
これは特別にGoogle側がIDの形式を指定していない=Azure側で選択可能であることを示しています。
【公式】シングル サインオンの SAML プロトコル:NameIDPolicy
SAML応答を確認
では、Azure側のSAML応答を見てみましょう。
<samlp:Response ID="_c2c73e1d-0900-4811-aa2c-2b8aa6a2ddea" Version="2.0" IssueInstant="2021-02-11T01:32:31.897Z" Destination="https://www.google.com/a/domain.jp/acs" InResponseTo="xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://sts.windows.net/xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxx/</Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><Assertion ID="_xxxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx" IssueInstant="yyyy-mm-ddT00:00:00.892Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://sts.windows.net/xxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxx/</Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue> Base64エンコードされたX509証明書 </X509Certificate></X509Data></KeyInfo></Signature> <Subject><NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">class.metaro@domain.jp</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><SubjectConfirmationData InResponseTo="mennehopinkcahoadednfdgfapmgelijiakhodhe" NotOnOrAfter="2021-02-11T02:32:31.742Z" Recipient="https://www.google.com/a/domain.jp/acs"/></SubjectConfirmation></Subject><Conditions NotBefore="yyyy-mm-ddT00:00:00.742Z" NotOnOrAfter="yyyy-mm-ddT00:00:31.742Z"><AudienceRestriction><Audience>google.com/a/domain.jp</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid"><AttributeValue>xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/objectidentifier"><AttributeValue>xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/displayname"><AttributeValue>クラス メタロウ</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider"><AttributeValue>https://sts.windows.net/xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/claims/authnmethodsreferences"><AttributeValue>http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"><AttributeValue>メタロウ</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"><AttributeValue>クラス</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><AttributeValue>class.metaro@domain.jp</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><AttributeValue>class.metaro@domain.jp</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="2021-02-11T01:32:30.103Z" SessionIndex="_6ad56e9d-a7d3-4d2a-9794-8d748c862a00"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion></samlp:Response>
Subjectの内容に注目してみましょう。
<NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">class.metaro@domain.jp</NameID>
うーん…ちゃんとnameidーformatではGoogle側の一意IDであるメールアドレスが返されています。これであればちゃんとGoogle側でユーザーが承認されるはずなのですが。
その次のAttributeStatementを見てみると…おやおや?なにか2バイトコードが返されています。
<Attribute Name="http://schemas.microsoft.com/identity/claims/displayname"><AttributeValue>クラス メタロウ</AttributeValue></Attribute> <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"><AttributeValue>メタロウ</AttributeValue></Attribute> <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"><AttributeValue>クラス</AttributeValue></Attribute>
Attribute Nameを見ると、クレームに最初から入っているdisplayname、givenname、surnameが返されているようです。
こうした場合は2バイトコードは多くの場合にトラブルのもとになることが多いため、ひとまず2バイトコードを無くしてみます。
AzureのSAML設定にあるユーザー属性とクレーム をメールアドレス要素だけを応答するよう、displayname、givenname、surnameを削除してみました。
結果はOK!
SAML応答のAttributeValueに2バイトコードが含まれていてはいけない、という資料は見つけることができませんでしたが、AzureADの場合ActiveDirectoryの情報から2バイトコードで姓名や表示名を設定していることが多いと思われます。
AzureAD SAML設定「ユーザー属性とクレーム 」には認証に必要な情報だけを応答する設定にしておく ことがトラブルを回避するために有効かもしれません。
SansanのSAML設定
AzureAD側の設定
Entity IDと Assertion Consumer Servie URL、サインイオンURLを設定します。
基本的なSAML構成
- 識別子:https://ap.sansan.com/saml2/(自社のドメイン名)
- 応答URL:https://ap.sansan.com/v/saml2/(自社のドメイン名)/acs
- サインオンURL:https://ap.sansan.com/
これらの情報はSansanの「管理者設定」→「セキュリティ設定」→「SAML認証」の「IdPに設定する情報」で表示されるものをそのまま貼り付けでOKです。サインオンURLはPC用を使用しています。
ユーザー属性とクレーム
SansanではGoogleの設定で得た知見を活かし、UserPrincipalnameを最小限にし、一意のユーザーIDは「user.mailnickname」を指定しました。
これも若干のハマリポイントでしたので後述します。
SAML署名証明書
GoogleWorkspaceと同様にSAML署名証明書からBase64証明書をダウンロードしてSansan管理者に渡します(画像は割愛)。
所有者、ユーザーとグループ
こちらも同様に「所有者」にAzureAD管理アカウントを、「ユーザーとグループ」には利用者全員が所属しているAzureのグループを割りあてます。
Sansan側の設定
SansanのSAML設定では以下を設定します。
AzureADのSAML設定「4 Sansanのセットアップ」で表示されている情報をコピペします。
- IdPの識別名:Azure AD 識別子
- ログインURL :ログインURL
- ログアウトURL:空欄
- SAML 署名証明書:ダウンロードしたBase64証明書を選択
※ログアウトURLを空欄にしておくと、Sansanからログアウトした際にAzureのログアウト画面ではなくSansanのログイン画面に戻るようにするためです。
動作の検証で「実行」をクリックしてSAML認証を実際にテストされます。正常であれば「保存」がクリックできるようになります。
テストが正常ではない場合、それまで設定した内容は保存されず、以前の設定が保持されています。
ハマリポイント
ここでも最初は正常に動作テストがうまくいきませんでした。
これもSAML要求、応答を取得して調べてみます。
SAML要求を確認
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceURL="https://ap.sansan.com/v/saml2/domain/acs" ID="_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" IssueInstant="yyyy-mm-ddT00:00:57Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0" ForceAuthn="true"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://ap.sansan.com/saml2/domain</saml:Issuer><samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"/></samlp:AuthnRequest>
こちらでもGoogleと同様にSPが要求しているユーザーIDのsamlp:NameIDPolicyはurn:oasis:names:tc:SAML:1.1:nameid-format:unspecified。
SAML応答を確認しながら試行錯誤が必要です。
SAML応答を確認
ちょっと悩みましたが、Sansanの設定ではシングルサインオンのユーザーIDをActiveDirectoryのユーザーID形式で指定していたことを思い出します。
そのため、NameIDはメールアドレスではなく、class.metaro@domain.jp の「class.metaro」だけであろう、と仮定(この設定は弊社の場合です)。
Azure SAML設定にあるユーザー属性とクレーム の「一意のユーザーID」を試行錯誤します。
SAML応答のNameIDが「class.metaro」になる値は「user.mailnickname」であることが分かり、結果として認証が正常に完了できました!
<samlp:Response ID="_90b8e5a0-d864-4d60-926b-5319b3bea96d" Version="2.0" IssueInstant="yyyy-mm-ddT00:00:15.072Z" Destination="https://ap.sansan.com/v/saml2/domain/acs" InResponseTo="_e997fbced51f4f5989ade95b985c96b5" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://sts.windows.net/xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/</Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><Assertion ID="_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" IssueInstant="yyyy-mm-ddT00:00:00.062Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://sts.windows.net/xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/</Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><Reference URI="#_xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>xxxxxxxxxxxxxxxxxxxxxxxxxxx</DigestValue></Reference></SignedInfo><SignatureValue> Base64エンコードされたX509証明書 </X509Certificate></X509Data></KeyInfo></Signature><Subject><NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">class.metaro</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><SubjectConfirmationData InResponseTo="_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" NotOnOrAfter="yyyy-mm-ddT00:00:00.887Z" Recipient="https://ap.sansan.com/v/saml2/domain/acs"/></SubjectConfirmation></Subject><Conditions NotBefore="yyyy-mm-ddT00:00:00.887Z" NotOnOrAfter="yyyy-mm-ddT00:00:14.887Z"><AudienceRestriction><Audience>https://ap.sansan.com/saml2/domain</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid"><AttributeValue>xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/objectidentifier"><AttributeValue>xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider"><AttributeValue>https://sts.windows.net/xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/</AttributeValue></Attribute><Attribute Name="http://schemas.microsoft.com/claims/authnmethodsreferences"><AttributeValue>http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password</AttributeValue><AttributeValue>http://schemas.microsoft.com/claims/multipleauthn</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><AttributeValue>class.metaro@domain.jp</AttributeValue></Attribute><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><AttributeValue>class.metrao@domain.jp</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="yyyy-mm-ddT00:00:08.457Z" SessionIndex="_xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion></samlp:Response>
NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">class.metaro</NameID>
今回はSaaS側で認証しているIDのフォーマットを把握しておくことも重要だという気付きの事例でした。
おわりに
最後までご覧いただきありがとうございました。
SAMLの設定自体は簡単である反面、動作しなかったときの根本原因はなかなかわかりにくものだ、ということはご理解いただけたと思います。
SAMLは自由度が高いため、SaaSプロバイダーによって扱われている用語や設定個所が分かりにくいことも往々にしてあります。
やはり動作させてみてSAML要求、SAML応答などを確認しながら動作確認したほうがいいと思いました。
業務影響が大きいシステムであれば、可能であれば検証環境を準備しておくほうがいいと感じました。
そしてやっぱり大事だな~と振り返るのは以下の2点。
- 複数名で情報を見ながら作業すること
- ちゃんとSAMLの動作仕様、XMLの見方を知っておくこと
特に前者。一人では作業しつつ冷静にログを見たりすることができませんし、違う目線・知見が入ることはとても重要だと思いました。
弊社 情報システムでは社内システムのシングルサインオン化を推進しています。
また今後はMDMと連携した条件付きアクセスを導入していく予定のため、事例を紹介できればと思います。